using System;
using System.Drawing;
using System.Drawing.Imaging;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// The unsharp operator code.
    /// </summary>
    public class Unsharp : BaseImageFilter
    {
        protected double m_dKUnsharp = 0.4f;

        /// <summary>
        /// The all 1s kernel.
        /// </summary>
        protected int[,] m_aKernel = { {1, 1, 1}, {1, 1, 1}, {1, 1, 1} };

        /// <summary>
        /// Initializes a new instance of the <see cref="Unsharp"/> class.
        /// </summary>
        public Unsharp()
            : this(0.4f)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Unsharp"/> class.
        /// </summary>
        /// <param name="_kUnsharp">The _k unsharp.</param>
        public Unsharp(double _kUnsharp)
        {
            this.m_dKUnsharp = _kUnsharp;
        }

        /// <summary>
        /// used to create a deep copy
        /// </summary>
        /// <param name="_unsharp"></param>
        internal Unsharp(Unsharp _unsharp)
        {
            m_dKUnsharp = _unsharp.m_dKUnsharp;
            m_aKernel = _unsharp.m_aKernel;
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            UnsafeBitmap bitmapResult = unsharp_image(_bitmap, this.m_dKUnsharp);
            BitmapFunctions.CopyUnsafe(bitmapResult, _bitmap);
            bitmapResult.Dispose();
            return _bitmap;
        }

        /// <summary>
        /// Gets or sets the kernel.
        /// </summary>
        /// <value>The kernel.</value>
        public int[,] Kernel
        {
            get { return m_aKernel; }
            set { m_aKernel = value; }
        }

        /// <summary>
        /// Gets or sets the level.
        /// </summary>
        /// <value>The level.</value>
        public double Level
        {
            get { return m_dKUnsharp; }
            set { m_dKUnsharp = value; }
        }

        /// <summary>
        /// subtracts the pixel.
        /// </summary>
        /// <param name="pixels1">The pixels1.</param>
        /// <param name="pixels2">The pixels2.</param>
        /// <returns></returns>
        protected UnsafeBitmap PixelSubtraction(UnsafeBitmap _bitmap1, UnsafeBitmap _bitmap2)
        {
            UnsafeBitmap bitmapResult = (UnsafeBitmap)_bitmap1.Clone();
            int y = 0;
            int x = 0;
            int nWidth = _bitmap1.Width;
            int nHeight = _bitmap1.Height;

            Color color1 = Color.Transparent;
            Color color2 = Color.Transparent;

            int nR1 = 0;
            int nG1 = 0;
            int nB1 = 0;
            
            int nR2 = 0;
            int nG2 = 0;
            int nB2 = 0;

            for (y = 0; y < nHeight; y++)
            {
                for (x = 0; x < nWidth; x++)
                {
                    color1 = _bitmap1.GetPixel(x, y);
                    nR1 = color1.R;
                    nG1 = color1.G;
                    nB1 = color1.B;

                    color2 = _bitmap2.GetPixel(x, y);
                    nR2 = color2.R;
                    nG2 = color2.G;
                    nB2 = color2.B;

                    int nR = nR1 - nR2;
                    int nG = nG1 - nG2;
                    int nB = nB1 - nB2;
                    if (nR < 0) nR = 0;
                    if (nG < 0) nG = 0;
                    if (nB < 0) nB = 0;
                    bitmapResult.SetPixel(x, y, Color.FromArgb(nR, nG, nB));
                }
            }
            return bitmapResult;
        }

        /// <summary>
        /// Adds each pixel in pixels2 to the corresponding pixel in pixels1.         
        /// </summary>
        /// <param name="pixels1">pixels1 the first array of pixels</param>
        /// <param name="pixels2">pixels2 the pixels to add to pixels1</param>
        /// <returns>the resulting array of pixel values</returns>
        protected UnsafeBitmap PixelAddition(UnsafeBitmap _bitmap1, UnsafeBitmap _bitmap2)
        {
            UnsafeBitmap bitmapResult = (UnsafeBitmap)_bitmap1.Clone();
            int y = 0;
            int x = 0;
            int nWidth = _bitmap1.Width;
            int nHeight = _bitmap1.Height;

            Color color1 = Color.Transparent;
            Color color2 = Color.Transparent;

            int nR1 = 0;
            int nG1 = 0;
            int nB1 = 0;

            int nR2 = 0;
            int nG2 = 0;
            int nB2 = 0;

            for (y = 0; y < nHeight; y++)
            {
                for (x = 0; x < nWidth; x++)
                {
                    color1 = _bitmap1.GetPixel(x, y);
                    nR1 = color1.R;
                    nG1 = color1.G;
                    nB1 = color1.B;

                    color2 = _bitmap2.GetPixel(x, y);
                    nR2 = color2.R;
                    nG2 = color2.G;
                    nB2 = color2.B;

                    int nR = nR1 + nR2;
                    int nG = nG1 + nG2;
                    int nB = nB1 + nB2;
                    if (nR > 255) nR = 255;
                    if (nG > 255) nG = 255;
                    if (nB > 255) nB = 255;
                    bitmapResult.SetPixel(x, y, Color.FromArgb(nR, nG, nB));
                }
            }
            return bitmapResult;
        }

        /// <summary>
        /// Multiplies each pixel in pixels with the constant.
        /// </summary>
        /// <param name="pixels">pixels the array of pixels</param>
        /// <param name="k">the multiplication constant</param>
        /// <returns>return the new array</returns>
        protected UnsafeBitmap PixelMultiplication(UnsafeBitmap _bitmap, double k)
        {
            int y = 0;
            int x = 0;
            int nWidth = _bitmap.Width;
            int nHeight = _bitmap.Height;

            int nR = 0;
            int nG = 0;
            int nB = 0;

            Color color = Color.Transparent;
            for (y = 0; y < nHeight; y++)
            {
                for (x = 0; x < nWidth; x++)
                {
                    color = _bitmap.GetPixel(x, y);

                    nR = (int)(color.R * k);
                    nG = (int)(color.G * k);
                    nB = (int)(color.B * k);
                    if (nR > 255) nR = 255;
                    if (nG > 255) nG = 255;
                    if (nB > 255) nB = 255;
                    _bitmap.SetPixel(x, y, Color.FromArgb(nR, nG, nB));
                }
            }
            return _bitmap;
        }

        /// <summary>
        /// Takes an image and applies the unsharp filter to it.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <param name="k">k the unsharp constant</param>
        /// <returns>the new image array</returns>
        protected UnsafeBitmap unsharp_image(UnsafeBitmap _bitmap, double k)
        {
            UnsafeBitmap bitmapResult = (UnsafeBitmap)_bitmap.Clone();
            for (int i = 0; i < 10; i++)
            {
                new GaussianBlur(12).Execute(bitmapResult);
            }

            bitmapResult = PixelSubtraction(bitmapResult, _bitmap);
            bitmapResult = PixelMultiplication(bitmapResult, k);
            bitmapResult = PixelAddition(bitmapResult, _bitmap);
            return bitmapResult;
        }

        /// <summary>
        /// Clones this instance
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new Unsharp(this);
        }
    }
}